#include "common.h"
#include "Vfifo.h"
#define MAX_WAVE 10000
#define TRACE 1
Vfifo top;
VerilatedFstC *tfp;
static uint64_t dump_time = 0;
void dump()
{
#if TRACE
    if (dump_time < MAX_WAVE)
    {
        tfp->dump(dump_time++);
    }
#endif
}

void run()
{
    top.clk = 0;
    top.eval();
    dump();
    top.clk = 1;
    top.eval();
}

void reset()
{
    top.rst = 1;
    run();
    run();
    run();
    top.rst = 0;
}

bool push(uint64_t data)
{
    top.i_valid = 1;
    top.i_data = data;
    if (top.i_ready & top.i_valid)
    {
        // LOG("push %ld",data);
        return true;
    }
    else
    {
        return false;
    }
}
uint64_t front()
{
    return top.o_data;
}
bool pop()
{
    top.o_ready = 1;
    if (top.o_ready & top.o_valid)
    {
        // LOG("pop  %ld",front());
        return true;
    }
    else
    {
        return false;
    }
}

void fifo_test(uint32_t r1, uint32_t r2)
{
    LOG("test full push %d%% pop %d%% at %ld", r1, r2, dump_time);
    reset();
    int test_cnt = 1000000;
    uint64_t push_data = 0;
    uint64_t pop_data = 0;
    while (test_cnt--)
    {
        top.i_valid = 0;
        top.o_ready = 0;
        uint32_t r = rand();
        r %= 100;
        if (r < r1)
        {
            bool success = push(push_data);
            if (success)
            {
                push_data++;
            }
        }
        r = rand();
        r %= 100;
        if (r < r2)
        {
            bool success = pop();
            if (success)
            {
                if (pop_data != front())
                {
                    LOG("pop data : %ld != o_data : %ld at %ld", pop_data, front(), dump_time);
                    return;
                }
                pop_data++;
            }
        }
        run();
    }
    LOG("test done");
}

int main()
{
    srand(0);

#if TRACE
    Verilated::traceEverOn(true);
    tfp = new VerilatedFstC();
    top.trace(tfp, 0);
    tfp->open("Vfifo.fst");
#endif
    reset();
    uint32_t test_loop = 10;
    while (test_loop--)
    {

        fifo_test(100, 1);
        fifo_test(1, 100);
        fifo_test(100, 10);
        fifo_test(10, 100);
        fifo_test(100, 100);
        fifo_test(50, 50);
        fifo_test(20, 50);
        fifo_test(50, 20);
        fifo_test(10, 5);
        fifo_test(5, 10);
        uint32_t rand_loop = 10;
        while (rand_loop--)
        {
            uint32_t r1 = rand();
            uint32_t r2 = rand();
            fifo_test(r1 % 100, r2 % 100);
        }
    }

#if TRACE
    tfp->close();
    delete tfp;
#endif
    LOG("All test done");
    return 0;
}